package qiongqi

import (
	"cdam/enum"
	"cdam/models"
	"cdam/qiongqi/factory"
	"cdam/qiongqi/listener"
	"cdam/util"
	"math/rand"
	"strconv"
	"strings"
	"time"
)

// 定义流程运行的接口； 目前只提供两个方法
type ProcessEnginee interface {

	// 启动流程
	StartWorkflowInstance() (int64,error)

	// 推动流程运行到下一步
	GotoNextTask() error

	// 根据消息启动流程
	StartProcessByMessage(message QiongqiMessage) error

	// 查找我的待办
	FindTodoProcess(candidateId int64,offset int64, limit int64) ([]*models.TodoProcess,int64,error)

	//
	ThrowMessage(message QiongqiMessage) error

	CatchMessage(message QiongqiMessage) error

	// 查找我的待办明细
	FindTodoProcessNodes(instanceId int64, instanceNodeId int64, candidateId int64) (interface{},error)
}

type MessageHeader struct {
	Name				string
	Type				enum.TopicType
	ProcessCode			string
	InstanceId			int64
}

type MessageBody struct {
	CurrentHander		string
	Parameter			map[string]interface{}
}

type QiongqiMessage struct {
	Header			MessageHeader
	Body			MessageBody
}

// 定义具体实现类
type Process struct {
	ProcessCode           	string      // 流程Code
	InstanceName          	string      // 流程实例Name

	CurrentTaskParameters 	interface{} // 任务传递的参数（节点参数）
	CurrentTaskHandler    	string      // 当前任务的处理人
	CurrentTaskInputParam 	interface{} // 当前任务节点用户输入的参数
	CurrentTaskId          	int64  		// 当前运行任务Id
	CurrentTaskBusinessKey 	string 		// 当前任务对应的表单ID


	NextTaskHandler        	string 		// 下一节点任务处理人
	NextTaskBusinessKey		string

	_ProcessId  			int64 		// 流程ID
	_InstanceId 			int64 		// 流程实例ID
	_CurrentNodeCode		string 		// 当前节点Code （查找路由使用）
	_CurrentNodeName		string		// 当前节点的名称
	_CurrentNodeId			int64		// 当前节点ID
	_TargetNodeCode			string 		// 要移动到的目标节点
	// _CurrentInstanceNodeCode string // 当前节点ID
	// _CurrentInstanceNodeName string // 当前节点名称
	// _CurrentInstanceNodeId   int64  // 当前节点的ID

	// _NextNodeCode string // 下一个流程实例节点Code
	// _NextNodeName string // 下一个流程实例节点名称
	// _NextNodeId   int64  // 下一个流程实例节点Id
	_listener 				listener.Listener			// 当前流程的监听
	BtnClient 				enum.BtnClientType			// 当前点击按钮类型
}

// process 结构体 必须包换 processEnginee 才能实现转换
var _ ProcessEnginee = new(Process)

func (p *Process) FindTodoProcessNodes(instanceId int64, instanceNodeId int64, candidateId int64) (interface{}, error) {
	list, err := models.GetTodoInstanceNode(instanceId,instanceNodeId,candidateId)
	return list, err
}

func (p *Process) FindTodoProcess(candidateId int64, offset int64, limit int64) ([]*models.TodoProcess,int64, error) {
	list ,count, err := models.GetTodoProcessByCandidateId(candidateId,offset,limit)
	return list,count, err
}

func (p *Process) StartWorkflowInstance() (int64,error) {
	// 判断流程Code是否 有效
	flow,err := models.GetFlowProcessByCode(p.ProcessCode)
	if err != nil {
		return 0, err
	}else{
		// 设置ProcessId
		p._ProcessId = flow.Id
	}
	// 设置监听
	listener := factory.GetListenerByName("workflow.mategroupapprove")
	listener.BeforInstanceStart()

	// 创建流程
	if err := p.CreateInstance(p.InstanceName); err == nil {

		// 创建开始节点
		if err := p.CreateStartInstanceNode(); err == nil {
			err = p.CommitToNextStep()
			listener.AfterInstanceStart()
			return flow.Id ,err
		}else{
			return -1,err
		}
	}else{
		return -1,err
	}
}

func (p *Process) GotoNextTask() error {
	// 查询 当前节点
	if instanceNode, err := models.GetFlowInstanceTaskById(p.CurrentTaskId); err== nil {
		// 设置当前节点为完成
		listen:= factory.GetListenerByName("workflow.mategroupapprove")
		_processNode, _ := models.GetFlowProcessNodeById(instanceNode.NodeId)
		p._listener = listen
		instanceNode.Status = enum.NodeFinished.String()
		instanceNode.Finishdate.Time = time.Now()
		p._ProcessId = instanceNode.ProcessId
		p._InstanceId = instanceNode.InstanceId
		p._CurrentNodeId = instanceNode.Id
		p._CurrentNodeCode = _processNode.Code

		param := listener.ListenParameter{}
		param.BtnClient = p.BtnClient
		param.CurrentNodeId = p.CurrentTaskId
		param.CurrentNodeParmeter = p.CurrentTaskParameters
		param.CandidateParameter = p.CurrentTaskInputParam
		param.CurrentNodeCode = _processNode.Code
		param = p._listener.BeforeNextStep(param)


		p.CurrentTaskInputParam = param.CandidateParameter
		p.CurrentTaskParameters = param.CurrentNodeParmeter
		// 设置当前节点参与人
		if err = p.SetCurrentNodeCandidate(false); err == nil {
			if err := models.UpdateFlowInstanceTaskById(instanceNode); err == nil {
				result := p.CommitToNextStep()
				// listen.AfterNextStep()
				return result
			}else{
				return err
			}
		}else{
			return err
		}
	}else{
		return err
	}

}

func (p *Process) StartProcessByMessage(message QiongqiMessage) error {
	panic("implement me")
}

func (p *Process) ThrowMessage(message QiongqiMessage) error {
	panic("implement me")
}

func (p *Process) CatchMessage(message QiongqiMessage) error {
	panic("implement me")
}

var p = new(Process)

type QiongqiProcessParam struct {
	ProcessCode            string      // 流程节点代码
	InstanceName           string      // 实例名称
	CurrentTaskParameters  interface{} // 启动任务传递的参数（节点参数）
	CurrentTaskHandler     string      // 当前任务的处理人
	CurrentTaskInputParam  interface{} // 当前任务参与者输入的参数
	NextTaskHandler        string      // 下一节点任务处理人
	CurrentTaskId          string      // 当前运行节点Id
	CurrentTaskBusinessKey string
	BtnClient              enum.BtnClientType
}
func NewQiongqiProcess(param QiongqiProcessParam) ProcessEnginee {
	if p == nil {
		p = new(Process)
	}
	p.ProcessCode = param.ProcessCode
	p.InstanceName = param.InstanceName

	p.CurrentTaskParameters = param.CurrentTaskParameters
	p.CurrentTaskHandler = param.CurrentTaskHandler
	p.CurrentTaskInputParam = param.CurrentTaskInputParam
	p.CurrentTaskBusinessKey = param.CurrentTaskBusinessKey

	p.NextTaskHandler = param.NextTaskHandler


	// p.NextFormKey = param.NextFormKey
	p.BtnClient = param.BtnClient
	if id , err := strconv.Atoi(param.CurrentTaskId); err == nil {
		p.CurrentTaskId = int64(id)
	}
	return p
}

// 创建流程并保存到数据库中
// @Param	flow   "工作流程model"
// @Param   instancename  "流程实例名称"
// @Return  int64 "流程实例ID"
// @Return  Error
func (p *Process)CreateInstance(instanceName string) error{
	var flowInstance = new (models.FlowInstance)
	flowInstance.Id = rand.New(util.Seed).Int63n(100000000)
	flowInstance.ProcessId = p._ProcessId
	flowInstance.Status = enum.PROCESSRUNNING.String()
	flowInstance.Createdate.Time = time.Now()
	flowInstance.LauncherDate.Time = time.Now()
	flowInstance.ProcessName = instanceName
	_,err := models.AddFlowInstance(flowInstance)
	if err != nil {
		return err
	}else{
		// 设置 instanceId
		p._InstanceId = flowInstance.Id
		return nil
	}
}

// 新增流程实例节点  自动创建流程开始任务 ； 任务状态 完成
// @Param	flow   "工作流程model"
// @Param   parameters  "传入参数"
// @Param   curHandler  "当前节点处理人"
// @Param   instanceId  "流程实例ID"
// @Return  curHandler "流程实例ID"
// @Return  Error

func (p *Process)CreateStartInstanceNode() error {
	var startNode = new(models.FlowInstanceTask)
	// 是否有相同的开始节点
	events := []string{enum.TimeEvent.String(), enum.StartEvent.String()}
	startFlowProcessNode ,err := models.GetFlowProcessNodeTypes(p._ProcessId,events,"")
	if err == nil {

		// 新增开始实例
		nodeId := rand.New(util.Seed).Int63n(100000000)
		startNode.Id = nodeId
		startNode.Createdate.Time = time.Now()

		// 开始任务参数
		startNode.InputData = util.ConverInterfaceToString(p.CurrentTaskParameters)

		startNode.Status = enum.NodeFinished.String()
		startNode.Finishdate.Time = time.Now()
		startNode.ProcessId = startFlowProcessNode.Processid
		startNode.InstanceId = p._InstanceId
		startNode.NodeId = startFlowProcessNode.Id
		startNode.NodeName = startFlowProcessNode.Name
		startNode.Finishdate.Time = time.Now()
		// 设置当前流程节点
		p.CurrentTaskId = nodeId
		p._CurrentNodeId = startFlowProcessNode.Id
		p._CurrentNodeCode = startFlowProcessNode.Code

		if err = p.SetCurrentNodeCandidate(true); err == nil {

			if _,err := models.AddFlowInstanceTask(startNode); err == nil {
				return p.SetTaskForm()
			}else{
				return err
			}
		}else{
			return err
		}
	}else {
		return err
	}
}

func (p *Process)SetTaskForm() error {
	instanceTaskForm := new (models.InstanceTaskForm)
	instanceTaskForm.Id = rand.New(util.Seed).Int63n(100000000)
	instanceTaskForm.Instanceid = p._InstanceId
	instanceTaskForm.Instancenodeid = p.CurrentTaskId
	if p.CurrentTaskBusinessKey != "" {
		instanceTaskForm.Formdata = p.CurrentTaskBusinessKey
	}else{
		instanceTaskForm.Formdata = ""
	}
	instanceTaskForm.Createdate = time.Now()
	instanceTaskForm.Updatedate = time.Now()

	_, err := models.AddInstanceForm(instanceTaskForm)

	return err
}

// 设置节点参与人
// @Param   curHandler   "当前节点处理人"
// @Param   startNodeId  "当前流程实例节点ID"
// @Param   instanceId   "流程实例ID"
// @Param   parameter    "用户输入参数"
// @Return  Error
func (p *Process)SetCurrentNodeCandidate(isFinish bool) error {
	if id, err := strconv.Atoi(p.CurrentTaskHandler); err == nil {
		// 获取当前节点的参与者
		nodeCandidate, count ,err  := models.GetOneFlowInstanceTaskCandidateByNode(p.CurrentTaskId,p._InstanceId, int64(id))
		if err == nil {
			// 当前任务没有参与者
			if count == 0  {
				nodeCandidate.Id = rand.New(util.Seed).Int63n(100000000)
				nodeCandidate.InstanceId = p._InstanceId
				// 只有完成状态 才能设置 参数
				if isFinish {
					nodeCandidate.InputData = util.ConverInterfaceToString(p.CurrentTaskInputParam)
					nodeCandidate.Finishdate = time.Now()
				}
				nodeCandidate.Status = enum.NodeFinished.String()
				nodeCandidate.Createdate = time.Now()

				nodeCandidate.CandidateId = int64(id)
				nodeCandidate.NodeId = p.CurrentTaskId

				if _,err = models.AddFlowInstanceTaskCandidate(&nodeCandidate); err == nil {
					return nil
				}else{
					return err
				}
			}else{
				nodeCandidate.Updatedate = time.Now()
				nodeCandidate.Finishdate = time.Now()
				nodeCandidate.InputData = util.ConverInterfaceToString(p.CurrentTaskInputParam)
				nodeCandidate.Status = enum.NodeFinished.String()
				if err = models.UpdateFlowInstanceTaskCandidateById(&nodeCandidate); err == nil {
					return nil
				}else{
					return err
				}
			}
		}else {
			return err
		}

	}else{
		return err
	}
}



// 新增流程实例节点
// @Param   nextHandler  "下一步参与人"
// @Param   instanceId   "流程实例ID"
// @Param   code         "流程节点代码"
// @Param   processId    "流程ID"
// @Param   param        "用户输入参数"
// @Return  curHandler   "流程ID"
// @Return  Error
func (p *Process) CommitToNextStep() error {
	// 获取当前节点的路由
	if routes, err := models.GetFlowProcessRouteCondition(p._ProcessId,"",p._CurrentNodeCode,""); err == nil {
		if len(routes) > 0 {
			for _,routeNode := range routes {
				// 根据表达式获取分支
				if routeNode.Expression != "" && len(routeNode.Expression) > 0 {
					maps,_ := p.CurrentTaskParameters.(map[string]interface{})
					if result , e := util.ExecuteQingQiuScript(routeNode.Expression, maps , routeNode.Code); e== nil {
						if result["result"].(bool) == true {
							p._TargetNodeCode = routeNode.Targeid
							return p.NextNodeHandler()
						}else{
							continue
						}
					}else{
						return e
					}
				}else{
					p._TargetNodeCode = routeNode.Targeid
					return p.NextNodeHandler()
				}
			}
			return nil
		}else{
			return err
		}
	}else{
		return err
	}
}

// 设置当前节点
// @Param   nextHandler  "下一步参与人"
// @Param   processId    "流程ID"
// @Param   instanceId   "流程实例ID"
// @Param   code         "下一个节点代码"
// @Param   param        "输入参数"
// @Param   inputdata    "下一步参与人输入参数"
func (p *Process) NextNodeHandler() error {
	// 获取流程节点信息
	if nodeDetail, err := models.GetFlowProcessNode(int(p._ProcessId),"",p._TargetNodeCode); err == nil {
		//  移动到本节点
		p._CurrentNodeId = nodeDetail.Id
		p._CurrentNodeCode = nodeDetail.Code
		p._CurrentNodeName = nodeDetail.Name

		if nodeDetail.TypeCode == enum.ExclusiveGateway.String() {
			return p.CommitToNextStep()
		}else if nodeDetail.TypeCode == enum.UserTask.String() {
			//  businesskey 清空
			p.CurrentTaskBusinessKey = ""
			return p.UserTaskHandler()
		}else if nodeDetail.TypeCode == enum.EndEvent.String() {
			return p.EndHandler()
		}else if nodeDetail.TypeCode == enum.StartEvent.String() || nodeDetail.TypeCode == enum.TerminateEvent.String() {
			return p.StartHandler()
		}else {
			return nil
		}
	}else{
		return err
	}
}

// 当前节点是 用户任务的处理
// @Param   nextHandler  "下一步参与人"
// @Param   nodeDetail   "要处理的流程节点"
// @Param   instanceId   "流程实例ID"
// @Param   inputdata    "下一步参与人输入参数"
func (p *Process) UserTaskHandler() error {
	var runningNode = new(models.FlowInstanceTask)
	runningNode.Id = rand.New(util.Seed).Int63n(100000000)
	runningNode.NodeName = p._CurrentNodeName
	runningNode.Status = enum.NodeRunning.String()
	runningNode.ProcessId = p._ProcessId
	runningNode.NodeId =  p._CurrentNodeId
	runningNode.Createdate.Time = time.Now()
	runningNode.InstanceId = p._InstanceId

	p.CurrentTaskId = runningNode.Id

	if err := p.SetNextNodeCandidate(); err == nil {
		if _, err := models.AddFlowInstanceTask(runningNode); err == nil {
			return p.SetTaskForm()
		} else {
			return err
		}
	}else{
		return err
	}
}

func (p *Process) EndHandler() error {
	var runningNode = new(models.FlowInstanceTask)
	runningNode.Id = rand.New(util.Seed).Int63n(100000000)
	runningNode.NodeName = p._CurrentNodeName
	runningNode.Status = enum.NodeFinished.String()
	runningNode.ProcessId = p._ProcessId
	runningNode.NodeId = p._CurrentNodeId
	runningNode.Createdate.Time = time.Now()
	runningNode.InstanceId = p._InstanceId
	runningNode.Finishdate.Time = time.Now()
	p.CurrentTaskId = runningNode.Id
	// 设置监听
	arterlistener  := factory.GetListenerByName("workflow.mategroupapprove")
	par := listener.ListenParameter{}
	par.BtnClient = p.BtnClient
	par.CurrentNodeId = p.CurrentTaskId
	par.CurrentNodeParmeter = p.CurrentTaskParameters
	par.CandidateParameter = p.CurrentTaskInputParam
	par.CurrentNodeCode = p._CurrentNodeCode
	par.InstanceId = p._InstanceId
	par.ProcessId = p._ProcessId
	arterlistener.BeforeEndInstance(par)

	p.CurrentTaskInputParam = ""
	p.SetCurrentNodeCandidate(true)
	if _, err := models.AddFlowInstanceTask(runningNode);  err == nil {
		if instanceNode,err := models.GetFlowInstanceById(p._InstanceId); err == nil {
			instanceNode.Status = enum.FINISH.String()
			instanceNode.FinishDate.Time = time.Now()
			if err = models.UpdateFlowInstanceById(instanceNode); err == nil {
				arterlistener.AfterEndInstance(par)
				return nil
			}else{
				return err
			}
		}else{
			return err
		}
	} else {
		return err
	}
}

func (p *Process) StartHandler() error {
	var runningNode = new(models.FlowInstanceTask)
	runningNode.Id = rand.New(util.Seed).Int63n(100000000)
	runningNode.NodeName = p._CurrentNodeName
	runningNode.Status = enum.NodeRunning.String()
	runningNode.ProcessId = p._ProcessId
	runningNode.NodeId = p._CurrentNodeId
	runningNode.Createdate.Time = time.Now()
	runningNode.InstanceId = p._InstanceId
	runningNode.Finishdate.Time = time.Now()
	p.CurrentTaskId = runningNode.Id
	if err := p.SetNextNodeCandidate(); err == nil {
		if _, err := models.AddFlowInstanceTask(runningNode); err == nil {
			return nil
		} else {
			return err
		}
	}else{
		return nil
	}
}

// 设置下一节点的参与人
func (p *Process) SetNextNodeCandidate() error {
	// 没有设置下一节点参与人
	if p.NextTaskHandler == "" || len(p.NextTaskHandler) == 0 {
		if assigners, err := models.GetFlowProcessNodeAssignByNode(p._ProcessId,p._CurrentNodeId); err == nil {
			taskCandidateParam := new(CandidateParam)
			taskCandidateParam.CurrentNodeId = p.CurrentTaskId
			taskCandidateParam.Status = enum.NodeRunning
			taskCandidateParam.ProcessId = p._ProcessId
			taskCandidateParam.InstanceId =p._InstanceId
			taskCandidateParam.UserParameter = util.ConverInterfaceToString(p.CurrentTaskInputParam)
			for _,assigner := range assigners {
				var expressionId int
				if expressionId,err = strconv.Atoi(assigner.Expression); err == nil {
					switch assigner.UserType {
					// 直接保存用户id
					case "candidateUser":
						taskCandidateParam.UserId = int64(expressionId)
						if err = p.CreateNextTaskCandidate(*taskCandidateParam); err != nil {
							return err
						}else{
							continue
						}
					// 保存用户角色
					case "candidateRole":
						taskCandidateParam.RoleId = int64(expressionId)
						if err = p.CreateTaskCandidateByRole(*taskCandidateParam); err != nil {
							return err
						}else{
							continue
						}
					case "candidateGroup":
						continue
					case "assignUser":
						taskCandidateParam.UserId = int64(expressionId)
						if err = p.CreateNextTaskCandidate(*taskCandidateParam); err != nil {
							return err
						}else{
							continue
						}
					default:
						continue
					}
				}else{
					return err
				}
			}
		}else{
			return err
		}
	}else{
		// 设置节点参与人情况
		handlers := strings.Split(p.NextTaskHandler,",")
		for _, handler := range handlers {
			if id, err := strconv.Atoi(handler); err == nil {
				var nodeCandidate models.FlowInstanceTaskCandidate
				nodeCandidate.Id = rand.New(util.Seed).Int63n(100000000)
				// nodeCandidate.InputData = util.ConverInterfaceToString(p.CurrentTaskInputParam)
				nodeCandidate.InstanceId = p._InstanceId
				nodeCandidate.Status = enum.NodeRunning.String()
				nodeCandidate.Createdate = time.Now()
				nodeCandidate.CandidateId = int64(id)
				nodeCandidate.NodeId = p.CurrentTaskId
				if _,err = models.AddFlowInstanceTaskCandidate(&nodeCandidate); err == nil {
					continue
				}else{
					return err
				}
			}else{
				return err
			}
		}
		return nil
	}
	return nil
}

type CandidateParam struct {
	UserId        int64                  // 参与人ID
	ProcessId     int64                  // 流程ID
	InstanceId    int64                  // 流程实例ID
	CurrentNodeId int64                  // 当前节点ID
	UserParameter string                 // 用户输入参数
	Status        enum.ProcessNodeStatus // 状态
	RoleId        int64                  //
	FormKey		  string
}
func (p *Process) CreateNextTaskCandidate(param CandidateParam) error {
	nodeCandidate := new(models.FlowInstanceTaskCandidate)
	nodeCandidate.Id = rand.New(util.Seed).Int63n(100000000)
	// nodeCandidate.InputData = param.UserParameter
	nodeCandidate.InstanceId = param.InstanceId
	nodeCandidate.Status = param.Status.String()
	nodeCandidate.Createdate = time.Now()
	nodeCandidate.CandidateId = param.UserId
	nodeCandidate.NodeId = param.CurrentNodeId
	nodeCandidate.FormKey = param.FormKey
	if _,err := models.AddFlowInstanceTaskCandidate(nodeCandidate); err == nil {
		return nil
	}else{
		return err
	}
}

func (p *Process) CreateTaskCandidateByRole(param CandidateParam) error {
	if users,err := models.GetUserByRole(param.RoleId); err == nil {
		for _,user := range users {
			param.UserId = int64(user.Id)
			if err = p.CreateNextTaskCandidate(param); err != nil {
				return err
			}
		}
		return nil
	}else{
		return err
	}
}

